package flex.cc.store.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

/*
 * 下游签名工具类
 * @version  
 * @since JDK 1.7 
 */
public class XYSdSignUtil {

    private static final Logger log = LoggerFactory.getLogger(XYSdSignUtil.class);

    public enum SignType {
        MD5, HMACSHA256
    }

    public static final String FAIL     = "FAIL";
    public static final String SUCCESS  = "SUCCESS";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";
    public static final String AES = "AES";
    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";
    public static final String EQUAL = "=";
    public static final String AMPERSAND = "&";
    /**
     * MD5:生成 MD5
     * 
     * @param data 待处理数据
     * @return MD5结果
     * @throws Exception
     * @since JDK 1.7
     */
    public static String MD5(String data) throws Exception {

        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * HMACSHA256:生成 HMACSHA256
     * 
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     * @since JDK 1.7
     */
    public static String HMACSHA256(String data, String key) throws Exception {

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     * 
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     * @throws Exception
     * @since JDK 1.7
     */
    public static String generateSignature(final Map<String, String> data, String key, String signType) throws Exception {

        Map<String, String> params = filterBlank(data);
        Set<String> keySet = params.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (params.get(k).trim().length() > 0) { // ����ֵΪ�գ��򲻲���ǩ��
                sb.append(k).append("=").append(params.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(key);
        if ( MD5.equals(signType)) {
            log.info("签名：" + MD5(sb.toString()).toUpperCase());
            return MD5(sb.toString()).toUpperCase();
        } else if (SignType.HMACSHA256.equals(signType)) {
            log.info("签名：" + HMACSHA256(sb.toString(), key));
            return HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }

    /**
     * 判断签名是否正确，必须包含sign字段，否则返回false。
     * 
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @since JDK 1.7
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, String signType) {

        if (!data.containsKey(FIELD_SIGN)) {
            return false;
        }
        String sign = data.get(FIELD_SIGN);
        try {
            return generateSignature(data, key, signType).equals(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }



    /**
     * coverMap2String:将Map中的数据转换成key1=value1&key2=value2的形式 不包含签名域 sign
     * 
     * @param data 待拼接的Map数据
     * @return 拼接好后的字符串
     * @since JDK 1.7
     */
    public static String coverMap2String(Map<String, String> data) {

        TreeMap<String, String> tree = new TreeMap<String, String>();
        Iterator<Entry<String, String>> it = data.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, String> en = it.next();
            if (FIELD_SIGN.equals(en.getKey().trim())) {
                continue;
            }
            tree.put(en.getKey(), en.getValue());
        }
        it = tree.entrySet().iterator();
        StringBuffer sf = new StringBuffer();
        while (it.hasNext()) {
            Entry<String, String> en = it.next();
            sf.append(en.getKey() + EQUAL + en.getValue() + AMPERSAND);
        }
        return sf.substring(0, sf.length() - 1);
    }

    /**
     * filterBlank:过滤请求报文中的空字符串或者空字符串
     * 
     * @param contentData 待过滤的数据
     * @return
     * @since JDK 1.7
     */
    public static Map<String, String> filterBlank(Map<String, String> contentData) {

        Map<String, String> submitFromData = new HashMap<String, String>();
        Set<String> keyset = contentData.keySet();
        for (String key : keyset) {
            String value = contentData.get(key);
            if (!isBlank(value)) {
                submitFromData.put(key, value.trim());
            }
        }
        return submitFromData;
    }

    public static boolean isBlank(String str) {
        if (str == null || str.trim().length() == 0) {
            return true;
        }
        return false;
    }
    /**
     * shortMD5:16位MD5加密算法
     * 
     * @param plainText
     * @return
     * @throws Exception
     * @since JDK 1.7
     */
    public static String shortMD5(String plainText) throws Exception {

        return MD5(plainText).substring(8, 24);
    }

    /**
     * 获取利用反射获取类里面的值和名称
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, String> objectToMap(Object obj) {
        Map<String, String> map = new HashMap<>();
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = null;
            try {
                value = field.get(obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if(value instanceof String){
                map.put(fieldName, (String) value);
            }else if(value instanceof List){
                map.put(fieldName, JSONObject.toJSONString(JSONArray.parseArray(JSON.toJSONString(value)),SerializerFeature.SortField,SerializerFeature.MapSortField));
            } else {
                map.put(fieldName, JSONObject.toJSONString(JSONObject.toJSON(value),SerializerFeature.SortField,SerializerFeature.MapSortField));
            }


        }
        return map;
    }


    /**
     *
     * String转map
     * @param str
     * @return
     */
    public static Map<String,String> getStringToMap(String str){
        //根据逗号截取字符串数组
        String[] str1 = str.split(",");
        //创建Map对象
        Map<String,String> map = new HashMap<>();
        //循环加入map集合
        for (int i = 0; i < str1.length; i++) {
            //根据":"截取字符串数组
            String[] str2 = str1[i].split(":");
            //str2[0]为KEY,str2[1]为值
            map.put(str2[0],str2[1]);
        }
        return map;
    }

    /**
     * bean 转化为实体
     *
     * @param bean
     * @return
     */
    public static Map<String, String> beanToStringMap(Object bean) {

        HashMap<String, String> map = new HashMap<String, String>();
        //获取指定类的BeanInfo 对象
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
            //获取所有的属性描述器
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                String key = pd.getName();
                if (!"class".equalsIgnoreCase(key)) {
                    Method getter = pd.getReadMethod();
                    Object value = getter.invoke(bean);
                    if (value != null) {
                        map.put(key, getValue(value));
                    }
                }
            }
        } catch (IllegalAccessException e) {} catch (IllegalArgumentException e) {} catch (InvocationTargetException e) {} catch (IntrospectionException e) {}
        return map;
    }

    /**
     * Format:yyyy-MM-dd HH:mm:ss
     */
    public static final String DATETIMESHOWFORMAT = "yyyy-MM-dd HH:mm:ss";


    private static String getValue(Object value) {

        String strValue;
        if (value instanceof String) {
            strValue = (String) value;
        } else if (value instanceof Integer) {
            strValue = ((Integer) value).toString();
        } else if (value instanceof Long) {
            strValue = ((Long) value).toString();
        } else if (value instanceof Float) {
            strValue = ((Float) value).toString();
        } else if (value instanceof Double) {
            strValue = ((Double) value).toString();
        } else if (value instanceof Boolean) {
            strValue = ((Boolean) value).toString();
        } else if (value instanceof Date) {
            strValue = format((Date) value, DATETIMESHOWFORMAT);
        } else {
            strValue = JSON.toJSONString(value);
        }
        return strValue;
    }


    /**
     * Date类型转换成String
     *
     * @param date Date型日期
     * @param pattern 格式要求
     * @return 日期文字描述
     */
    public static String format(Date date, String pattern) {

        if (date == null) {
            return null;
        }
        DateFormat formatter = getDateFormat(pattern);
        return formatter.format(date);
    }
    /**
     * 获取正确的DateFormat对象
     */
    private static DateFormat getDateFormat(String pattern) {

        DateFormat df = new SimpleDateFormat(pattern);
        df.setTimeZone(TimeZone.getDefault());
        return df;
    }
}
